/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 external_string.cpp

	$Header: /game/external_string.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "external_string.h"

#include <algorithm>
#include <assert.h>
#include <fstream>
#include <functional>
#include <utility>
#include <vector>

#include "string_insensitive_compare.h"
#include "string_table.h"

// --------------------------------------------------------------------------
// Unnamed namespace
// --------------------------------------------------------------------------

namespace
{

	typedef std::pair< std::string, std::string > t_string_pair;

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	std::vector< t_string_pair > & get_string_vector()
	{
		static std::vector< t_string_pair > string_vector;
		return string_vector;
	}

	// --------------------------------------------------------------------------
	// t_compare_keys function object class
	// --------------------------------------------------------------------------
	struct t_compare_keys
	{
		bool operator()( t_string_pair const & left, t_string_pair const & right ) const;
		bool operator()( t_string_pair const & left, std::string const & right ) const;
		bool operator()( std::string const & left, t_string_pair const & right ) const;
	};

	inline bool t_compare_keys::operator()( t_string_pair const & left, t_string_pair const & right ) const
	{
		return string_insensitive_compare( left.first, right.first ) < 0;
	}

	inline bool t_compare_keys::operator()( t_string_pair const & left, std::string const & right ) const
	{
		return string_insensitive_compare( left.first, right ) < 0;
	}

	inline bool t_compare_keys::operator()( std::string const & left, t_string_pair const & right ) const
	{
		return string_insensitive_compare( left, right.first ) < 0;
	}

} // Unnamed namespace

// --------------------------------------------------------------------------
// t_external_string members
// --------------------------------------------------------------------------

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_external_string::add_table( t_string_table const & new_string_table )
{
	std::vector< t_string_pair > & string_vector = get_string_vector();
	std::vector< t_string_pair > new_string_vector( string_vector.size() + new_string_table.size() );
	std::merge(
		string_vector.begin(),
		string_vector.end(),
		new_string_table.begin(),
		new_string_table.end(),
		new_string_vector.begin(),
		t_compare_keys() );
	new_string_vector.swap( string_vector );
}

#if !defined( NDEBUG )

t_external_string::t_instance_list_ptr t_external_string::get_list()
{
	static t_instance_list_ptr list( new t_instance_list );

	return list;
}

#endif // !defined( NDEBUG )

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_external_string::t_external_string( std::string const & key )
	:	m_string( key ),
		m_loaded( false )
{
	m_found = false;
#if !defined( NDEBUG )
	m_list = get_list();
	m_list->push_front( this );
#endif // !defined( NDEBUG )
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_external_string::load_string()
{
	assert( !m_loaded );

	std::vector< t_string_pair > & string_vector = get_string_vector();
	std::vector< t_string_pair >::const_iterator string_iter;
	string_iter =	std::lower_bound(
						string_vector.begin(),
						string_vector.end(),
						m_string,
						t_compare_keys() );
	if (	string_iter != string_vector.end()
		&&	string_insensitive_compare( string_iter->first, m_string ) == 0 )
	{
		m_string = string_iter->second;
#if !defined( NDEBUG )
		m_found = true;
#endif // !defined( NDEBUG )
	}
	// if not found, leave keyword as string, to make finding the error easier.
	m_loaded = true;
}

#if !defined( NDEBUG )

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_external_string::~t_external_string()
{
	m_list->remove( this );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
int t_external_string::load_all()
{
	t_instance_list_ptr list = get_list();

	t_instance_list::const_iterator instance_ptr_end = list->end();
	t_instance_list::const_iterator instance_ptr_iter = list->begin();
	t_external_string*					external_string;
	int									result = 0;
	std::fstream						stream;

	for ( ; instance_ptr_iter != instance_ptr_end; ++instance_ptr_iter )
	{
		external_string = *instance_ptr_iter;
		external_string->get();
		if (external_string->m_found)
			continue;

		if (result == 0)
		{
			stream.open( "missing_external_strings.txt", std::ios_base::out );
		}
		++result;
		stream << "Failed to find string: " << external_string->m_string << "\n";
	}
	// stream closes automatically if open.
	return result;
}

#endif // !defined( NDEBUG )
